package com.faner4cloud.yun.common.util.progress;


import com.faner4cloud.yun.common.util.progress.timer.TimerConfig;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.TimeUnit;
import java.util.function.BiConsumer;
import java.util.function.Consumer;


/**
 * 进度构造器
 */
public class ProgressBuilder {

	/**
	 * 未定义的进度名
	 */
	final static String IDENTITY_UNDEFINED = "undefined";

	/**
	 * 单个进度工作的唯一标识
	 */
	private final String identity;

	/**
	 * 定时器
	 */
	private TimerConfig timerConfig;

	/**
	 * 完成处理器
	 */
	private Consumer<Progress> thenAccept;

	/**
	 * 异常处理器
	 */
	private BiConsumer<Progress, ? super Throwable> errorConsumer;

	/**
	 * 任务队列
	 */
	private final List<AbstractTask> tasks = new ArrayList<>();

	private ProgressBuilder(String identity) {
		this.identity = identity;
	}

	public Progress build() {
		if (this.tasks == null || this.tasks.size() == 0) {
			throw new RuntimeException("执行任务不能为空");
		}
		BigDecimal r = BigDecimal.valueOf(0);
		BigDecimal hundred = BigDecimal.valueOf(100);
		int totalRate = this.tasks.stream().mapToInt(t -> t.getRate()).sum();
		for (int i = 0; i < this.tasks.size(); i++) {
			AbstractTask task = this.tasks.get(i);
			BigDecimal b = BigDecimal.valueOf(task.getRate() * 100).divide(BigDecimal.valueOf(totalRate), 2, RoundingMode.FLOOR);
			if (BigDecimal.ZERO.compareTo(b) == 0) {
				b = BigDecimal.ONE;
			}
			if (i == this.tasks.size() - 1) {
				b = hundred.subtract(r);
			}
			task.setRatePercent(b);
			r = r.add(b);
		}
		return new Progress(this.identity, this.tasks, this.timerConfig, this.thenAccept, this.errorConsumer);
	}

	public ProgressBuilder tasks(List<AbstractTask> tasks) {
		this.tasks.addAll(tasks);
		return this;
	}

	/**
	 * 以唯一进度标识构造进度构造类
	 * <p>
	 * 可以是个随机串，也可以是将参数集合排序后hash等等
	 * </p>
	 *
	 * @param identity
	 * @return ProgressBuilder
	 */
	public static ProgressBuilder identity(String identity) {
		return new ProgressBuilder(identity);
	}

	public ProgressBuilder thenAccept(Consumer<Progress> consumer) {
		this.thenAccept = consumer;
		return this;
	}

	public ProgressBuilder onError(BiConsumer<Progress, ? super Throwable> errorConsumer) {
		this.errorConsumer = errorConsumer;
		return this;
	}

	/**
	 * 配置定时器, 在进度完成或异常之前， 每 interval 的 unit 时 consume 一次, run 开始先执行一次再进行定时循环;
	 *
	 * @param unit           定时单位
	 * @param interval       定时时长
	 * @param timingConsumer 定时消费行为
	 * @return ProgressBuilder
	 */
	public ProgressBuilder timing(TimeUnit unit, long interval, Consumer<Progress> timingConsumer) {
		Objects.requireNonNull(unit, "定时器单位不能为空");
		Objects.requireNonNull(timingConsumer, "定时器消费不能为空");
		this.timerConfig = new TimerConfig(unit, interval, timingConsumer);
		return this;
	}
}
